package com.woniuxy.creditsystem.common.configure;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.woniuxy.creditsystem.shiro.LoginAndAuthRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro的配置类
 * 这些方法就相当于是在配置文件中的<bean>组件的功能，所以在方法头上要加@Bean
 * ---阅读顺序：从下往上看，比价容易看懂些
 */
@Configuration  //表明当前类是一个配置类
public class ShiroConfigure {

    /**
     * 配置Cookie管理器
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        byte[] cipherKey = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");
        rememberMeManager.setCipherKey(cipherKey);
        rememberMeManager.setCookie(rememberMeCookie());
        return rememberMeManager;
    }
    /**
     * 配置让Cookie实现 记住我 的功能
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        /*
         * cookie 的最大失效时间 7天，单位：秒 maxAge=-1 表示关闭浏览器cookie失效
         */
        cookie.setMaxAge(604800);
        //cookie存放的路径
        cookie.setPath("/");
        return cookie;
    }


    /**
     * 以下2个配置，是用来开启Shiro的注解支持,并提供AOP代理实现
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    /**
     * 配合上述的配置类，完成对使用了@RequiresPermissions,@RequiresRoles进行授权通知
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }



    @Bean
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager=new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");

        //还需要在此java文件另一个函数中让Realm支持shiro缓存

        return ehCacheManager;
    }

    /**
     * 用于Thymeleaf模板使用shiro标签，不加的话，shiro标签不生效
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    /**
     * 配置Shiro的 会话支持管理器
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        //该会话 跟HttpSession没有任何关系（该会话由shiro自带）
        //tomcat的HTTPSession：需要tomcat自己维护，很耗费内存和性能资源，所有有了shiro自带的session
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //默认会话超时时间为：30分钟（单位毫秒）
        sessionManager.setGlobalSessionTimeout(1800000);
        //删除失效的Session会话
        sessionManager.setDeleteInvalidSessions(true);
        //设置认证时，是否需要使用Session
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }


    /**配置shiro过滤器
     * 该过滤器作用：完成对页面请求的过滤，并针对请求，配置对应的过滤规则
     *
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean filterFactoryBean(){
        ShiroFilterFactoryBean factoryBean=new  ShiroFilterFactoryBean();
        //配置安全管理器（所有的请求，需要经过安全管理器）
        factoryBean.setSecurityManager(securityManager());
        //配置登录路径，登录成功路径，未授权路径
        //登录路径，
        factoryBean.setLoginUrl("/login");
        //登录成功路径
        factoryBean.setSuccessUrl("/loginOk");
        //未授权路径（老师说；当该路径可以点击--即显示出来了，然后没有权限的用户去点击，会跳转到这个unauthorized.html页面）
        factoryBean.setUnauthorizedUrl("/unauthorized");

        //配置shiro的过滤器链
        Map<String,String> filters=new LinkedHashMap<>();
        //anno表示匿名，无需认证，直接操作
//        filters.put("/", "anon");//首页页面
        filters.put("/login", "anon");//登录页面
        filters.put("/sys/login", "anon");//登录请求
        filters.put("/static/**", "anon");//静态资源
//        filters.put("/dept/more", "perms");//静态资源
        //authc代表 一定要认证（就是登录）
//        filters.put("/**", "authc");
        //user：表示是  用户的选择（比如“记住我”）
        filters.put("/**", "user");

        //将过滤规则，设置给Shiro工厂
        factoryBean.setFilterChainDefinitionMap(filters);

        return factoryBean;
    }

    /**
     * 配置 身份匹配器
     * 作用：判断token信息  和info信息是否一致
     * @return
     */
    @Bean
    public HashedCredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        //密码的加密方式为 MD5（是一种不可逆的加密方式：只能加密，不能解密）
        credentialsMatcher.setHashAlgorithmName("MD5");
        //针对MD5加密的信息，再加密1024次。设置加密次数（Iterations迭代次数，可以自己改，但是自己要记住加了多少次密）
        credentialsMatcher.setHashIterations(1024);
        return credentialsMatcher;
    }

    /**
     * 安全管理器
     * 相当于SpringMVC框架的DispathtcherServlet（前端处理器）
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //可以管理多个：当有多个登录入口的时候，管理多个
        //告诉安全管理器，认证和授权的对象是哪个
        //loginAndAuthRealm() 配置shiro框架中，用来完成身份认证、授权的 “域对象”
        securityManager.setRealm(loginAndAuthRealm());
        //如果有多个认证、授权对象，请使用securityManager.setRealm(集合)

        //会话
        //sessionManager()是  配置Shiro的 会话支持管理器
        securityManager.setSessionManager(sessionManager());

        //将缓存管理器，交给安全管理器
        securityManager.setCacheManager(ehCacheManager());
        //将“记住我管理器”配置给安全管理器
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 配置shiro框架中，用来完成身份认证、授权的 域对象
     * LoginAndAuthRealm.java---是自定义的类，继承自AuthorizingRealm
     * @return
     */
    @Bean
    public LoginAndAuthRealm loginAndAuthRealm(){
        LoginAndAuthRealm realm=new LoginAndAuthRealm();
        //给Realm 中配置 身份对比规则
        //credentialsMatcher()是配置身份匹配器，判断token信息  和info信息是否一致
        realm.setCredentialsMatcher(credentialsMatcher());

        //让Realm支持缓存
        realm.setCachingEnabled(true);
        //配置认证信息，需要使用缓存
        realm.setAuthenticationCachingEnabled(true);
        realm.setAuthenticationCacheName("authenticationCache");
        //配置授权信息，需要使用缓存
        realm.setAuthorizationCachingEnabled(true);
        realm.setAuthorizationCacheName("authorizationCache");

        return realm;
    }


    /**
     * 管理subject主体对象，生命周期的组件
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
}
